package com.gadgets.busi.bm02.service;

import com.gadgets.busi.bm02.common.GadgetsBm02Constant;
import com.gadgets.busi.bm02.common.GadgetsBm02Utils;
import com.gadgets.busi.bm02.dto.*;
import com.gadgets.busi.bm02.pojo.MaterialDict;
import com.gadgets.busi.bm02.pojo.MaterialRecipe;
import com.gadgets.busi.bm02.repository.IMaterialDictRepository;
import com.gadgets.busi.bm02.repository.IMaterialRecipeRepository;
import com.gadgets.framework.common.constant.GadgetsExceptionConstant;
import com.gadgets.framework.common.dto.Pagination;
import com.gadgets.framework.common.exception.GadgetsServiceException;
import com.gadgets.framework.common.util.PaginationUtil;
import com.gadgets.framework.common.util.PinYinUtil;
import com.gadgets.framework.common.util.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author UncleXiu
 * @version V1.0.0
 * @data 2020-11-02
 *
 */
@Service
@Transactional(transactionManager = "gadgetsDbTransactionManage", rollbackFor = {Throwable.class})
public class McMaterialServiceImpl implements IMaterialService{

    private static final Logger logger = LoggerFactory.getLogger(McMaterialServiceImpl.class);

    @Autowired
    private IMaterialRecipeRepository materialRecipeRepository;
    @Autowired
    private IMaterialDictRepository materialDictRepository;
    @Autowired
    private GadgetsBm02Utils gadgetsBm02Utils;

    /**
     * 新增合成表信息
     *
     * @param recipeInfoDTO 合成表信息
     *
     * @return 新增记录数
     *
     * @throws GadgetsServiceException 异常信息
     */
    @Transactional(transactionManager = "gadgetsDbTransactionManage", rollbackFor = {Throwable.class})
    @Override
    public void addMeterialRecipe(RecipeInfoDTO recipeInfoDTO) throws GadgetsServiceException {
        // 组装合成表信息
        for (RecipeItemDTO recipeItemDTO : recipeInfoDTO.getList()) {
            MaterialRecipe recipe = buildRecipeInfo(recipeInfoDTO,recipeItemDTO);
            materialRecipeRepository.insertRecipe(recipe);
        }
    }

    /**
     * 编辑合成表信息
     *
     * @param recipeInfoDTO 合成表信息
     *
     * @return 更新后记录数
     *
     * @throws GadgetsServiceException 异常信息
     */
    @Transactional(transactionManager = "gadgetsDbTransactionManage", rollbackFor = {Throwable.class})
    @Override
    public void editMeterialRecipe(RecipeInfoDTO recipeInfoDTO) throws GadgetsServiceException {
        // 删除旧合成表
        materialRecipeRepository.deleteRecipe(recipeInfoDTO.getMeterialId());
        // 组装合成表信息
        for (RecipeItemDTO recipeItemDTO : recipeInfoDTO.getList()) {
            MaterialRecipe recipe = buildRecipeInfo(recipeInfoDTO, recipeItemDTO);
            materialRecipeRepository.insertRecipe(recipe);
        }
    }

    /**
     * 查询合成表信息
     *
     * @param recipeQueryDTO 查询条件
     *
     * @return 查询结果
     *
     * @throws GadgetsServiceException 异常信息
     */
    @Transactional(transactionManager = "gadgetsDbTransactionManage", rollbackFor = {Throwable.class}, propagation = Propagation.SUPPORTS)
    @Override
    public List<MaterialRecipe> selectMaterialRecipeByCondition(RecipeQueryDTO recipeQueryDTO) throws GadgetsServiceException {
        return materialRecipeRepository.selectMaterialRecipeByCondition(recipeQueryDTO);
    }

    /**
     * 构建合成项目信息
     * @param recipeInfoDTO     合成信息
     * @param recipeItemDTO     合成表项目
     * @return                  合成表信息
     */
    protected MaterialRecipe buildRecipeInfo(RecipeInfoDTO recipeInfoDTO, RecipeItemDTO recipeItemDTO) {
        MaterialRecipe recipe = new MaterialRecipe();
        recipe.setTargetMeterialId(recipeInfoDTO.getMeterialId());
        recipe.setMateriaId(recipeItemDTO.getMeterialId());
        if(recipeItemDTO.getDemandNum()>64){
            throw new GadgetsServiceException(GadgetsExceptionConstant.VALIDATE_ERROR,"合成原料数量不能超过64个");
        }
        recipe.setDemandNum(recipeItemDTO.getDemandNum());
        return recipe;
    }


    /**
     * 分页查询物品列表
     * @param queryDTO                  查询条件
     * @param pagination                分页信息
     * @return                          查询结果
     * @throws GadgetsServiceException  异常信息
     */
    @Transactional(transactionManager = "gadgetsDbTransactionManage", rollbackFor = {Throwable.class}, propagation = Propagation.SUPPORTS)
    @Override
    public List<MaterialDict> selectMaterialDictByCondition(MaterialDictQueryDTO queryDTO, Pagination pagination) throws GadgetsServiceException{
        PaginationUtil.bulidPaginationInfo(pagination);
        List<MaterialDict> resultList = materialDictRepository.selectAllMaterialDictByCondition(queryDTO);
        PaginationUtil.getPaginationResultInfo(resultList,pagination);
        return resultList;
    }


    /**
     * 新增物品信息
     *
     * @param materialAddInfoDTO 物品信息
     *
     * @throws GadgetsServiceException 异常信息
     */
    @Override
    @Transactional(transactionManager = "gadgetsDbTransactionManage", rollbackFor = {Throwable.class})
    public void addMaterialInfo(MaterialAddInfoDTO materialAddInfoDTO) throws GadgetsServiceException {
        // 保存物品信息
        MaterialDict dict = new MaterialDict();
        dict.setModName(materialAddInfoDTO.getMaterialModName());
        dict.setDictName(materialAddInfoDTO.getMaterialName());
        dict.setOriFlag(materialAddInfoDTO.getMaterialOriFlag());
        dict.setPinyin(PinYinUtil.pinYintrasnlate(materialAddInfoDTO.getMaterialName()));
        dict.setWeight(1.0);
        materialDictRepository.saveMaterialDict(dict);
        // 如果物品不是原始物品，则保存合成表信息
        dict.setId(dict.getId());
        if(GadgetsBm02Constant.ORI_FLAG_NO.equals(dict.getOriFlag())) {
            addMaterialRecipe(materialAddInfoDTO, dict.getId());
        }
        gadgetsBm02Utils.addMcMeterialDictItem(dict);
    }

    /**
     * 创建合成表信息
     * @param materialAddInfoDTO            合成表信息
     * @param targetId                      新物品ID
     * @throws GadgetsServiceException      异常信息
     */
    protected void addMaterialRecipe(MaterialAddInfoDTO materialAddInfoDTO, long targetId) throws GadgetsServiceException{
        List<MaterialAddRecipeDTO> recipeDTOList = materialAddInfoDTO.getRecipeArr();
        // 如果存在多个合成表
        int i  = 0;
        for (MaterialAddRecipeDTO materialAddRecipeDTO : recipeDTOList) {
            String energyType = materialAddRecipeDTO.getEnergyType();
            Double energyNum = materialAddRecipeDTO.getEnergyNum();
            Integer prodUnit = materialAddRecipeDTO.getProdUnit();
            List<MaterialAddRecipeItemDTO> itemDTOList = materialAddRecipeDTO.getRecipeItems();
            // 遍历合成项目表
            for (MaterialAddRecipeItemDTO materialAddRecipeItemDTO : itemDTOList) {
                MaterialRecipe recipeItem = new MaterialRecipe();
                recipeItem.setMateriaId(materialAddRecipeItemDTO.getSourceId());
                recipeItem.setTargetMeterialId(targetId);
                recipeItem.setBatchNo(materialAddInfoDTO.getMaterialName()+i);
                recipeItem.setGoodNum(prodUnit);
                recipeItem.setDemandNum(materialAddRecipeItemDTO.getNum());
                recipeItem.setEnergyNum(energyNum * GadgetsBm02Constant.ENERGY_TRANSLATE_TYPE.valueOf(energyType).getRate());
                materialRecipeRepository.insertRecipe(recipeItem);
            }
            i++;
        }
    }

    /**
     * 物品信息和合成表导出
     * @param targetPath 导出目标地址
     * @throws GadgetsServiceException 异常信息
     */
    @Transactional(transactionManager = "gadgetsDbTransactionManage", rollbackFor = {Throwable.class}, propagation = Propagation.SUPPORTS)
    @Override
    public void exportMaterialAndRecipeInfo(String targetPath) throws GadgetsServiceException {
        HashMap<String, Document> docMap = new HashMap<>();
        // 先查询一次，以获取总页数
        int i = 1;
        Pagination pagination = new Pagination();
        pagination.setCurrentPage(i);
        pagination.setPageSize(50);
        PaginationUtil.bulidPaginationInfo(pagination);
        List<MaterialDict> list = materialDictRepository.selectAllMaterialDict();
        PaginationUtil.getPaginationResultInfo(list, pagination);
        RecipeQueryDTO queryDTO = new RecipeQueryDTO();
        queryDTO.addAsc("BATCH_NO");
        while (true){
            // 导出查询列表
            exportMaterialDictInfo(docMap, list, queryDTO);
            i++;
            // 页码+1后，如果还没超出最后一页，则再次查询，并再循环一次
            if(i<=pagination.getTotalPage()) {
                pagination.setCurrentPage(i);
                PaginationUtil.bulidPaginationInfo(pagination);
                list = materialDictRepository.selectAllMaterialDict();
                PaginationUtil.getPaginationResultInfo(list, pagination);
            }else {
                // 页码已超出最大页码，结束循环
                break;
            }
        }
        // 将Document写入到文件
        writeMaterialXmlFile(docMap,targetPath);
    }

    @Transactional(transactionManager = "gadgetsDbTransactionManage", rollbackFor = {Throwable.class})
    @Override
    public void pinyinGenerator() throws GadgetsServiceException {
        int i = 1;
        Pagination pagination = new Pagination();
        pagination.setCurrentPage(i);
        pagination.setPageSize(50);
        PaginationUtil.bulidPaginationInfo(pagination);
        List<MaterialDict> list = materialDictRepository.selectAllMaterialDict();
        PaginationUtil.getPaginationResultInfo(list, pagination);
        RecipeQueryDTO queryDTO = new RecipeQueryDTO();
        queryDTO.addAsc("BATCH_NO");
        while (true){
            // 页码+1后，如果还没超出最后一页，则再次查询，并再循环一次
            if(i<=pagination.getTotalPage()) {
                pagination.setCurrentPage(i);
                PaginationUtil.bulidPaginationInfo(pagination);
                list = materialDictRepository.selectAllMaterialDict();
                for (MaterialDict materialDict : list) {
                    // 只有拼音列为空，才执行此方法
                    if(StringUtils.isBlank(materialDict.getPinyin())){
                        logger.info("物品ID：{}，名称：{} 的拼音为空，开始生成拼音",materialDict.getId(),materialDict.getDictName());
                        materialDict.setPinyin(PinYinUtil.pinYintrasnlate(materialDict.getDictName()));
                        logger.info("物品ID：{}，名称：{} 的拼音为{}",materialDict.getId(),materialDict.getDictName(),materialDict.getPinyin());
                        materialDictRepository.updateMaterialDictPinYin(materialDict);
                    }
                }
                PaginationUtil.getPaginationResultInfo(list, pagination);
            }else {
                // 页码已超出最大页码，结束循环
                break;
            }
            i++;
        }
    }

    /**
     * 导出物品字典信息
     * @param docMap        Document容器
     * @param list          当前列表
     * @param queryDTO      查询条件
     */
    protected void exportMaterialDictInfo(HashMap<String, Document> docMap, List<MaterialDict> list, RecipeQueryDTO queryDTO) {
        for (MaterialDict materialDict : list) {
            if(!docMap.containsKey(materialDict.getModName())){
                Document document = DocumentHelper.createDocument();
                document.addElement(GadgetsBm02Constant.MATERIAL_CONFIG.ELE_MATERIALS)
                        .addAttribute(GadgetsBm02Constant.MATERIAL_CONFIG.ATTR_MATERIALS_MOD,materialDict.getModName());
                docMap.put(materialDict.getModName(), document);
            }
            Document doc = docMap.get(materialDict.getModName());
            // 创建物品标签
            Element materialElement = doc.getRootElement().addElement(GadgetsBm02Constant.MATERIAL_CONFIG.ELE_MATERIAL);
            materialElement.addAttribute(GadgetsBm02Constant.MATERIAL_CONFIG.ATTR_MATERIAL_NAME,materialDict.getDictName());
            materialElement.addAttribute(GadgetsBm02Constant.MATERIAL_CONFIG.ATTR_MATERIAL_ORI,GadgetsBm02Constant.ORI_FLAG_YES.equals(materialDict.getOriFlag())?"true":"false");
            materialElement.addAttribute(GadgetsBm02Constant.MATERIAL_CONFIG.ATTR_MATERIAL_MOD,materialDict.getModName());
            // 解析合成表
            exportRecipeInfo(queryDTO, materialDict, materialElement);
        }
    }

    /**
     * 写XML文件
     * @param docMap    Document容器
     */
    protected void writeMaterialXmlFile(HashMap<String, Document> docMap,String targetPath) {
        OutputFormat format = OutputFormat.createPrettyPrint();
        format.setEncoding("UTF-8");
        for (Map.Entry<String, Document> docEntry : docMap.entrySet()) {
            Writer writer = null;
            XMLWriter xmlWriter = null;
            try {
                writer = new FileWriter(new File(targetPath,docEntry.getKey()+".material.xml"));
                xmlWriter = new XMLWriter(writer,format);
                xmlWriter.write(docEntry.getValue());
            } catch (IOException e) {
                logger.error(StringUtils.formatExceptionStack(e));
            } finally {
                if(null!=writer){
                    try {
                        writer.close();
                    } catch (IOException e) {
                        logger.error(StringUtils.formatExceptionStack(e));
                    }
                }
                if(null!=xmlWriter){
                    try {
                        xmlWriter.close();
                    } catch (IOException e) {
                        logger.error(StringUtils.formatExceptionStack(e));
                    }
                }
            }
        }
    }

    /**
     * 导出合成表信息
     * @param queryDTO          合成信息查询条件
     * @param materialDict      原料字典
     * @param materialElement   原料所属XML节点
     */
    protected void exportRecipeInfo(RecipeQueryDTO queryDTO, MaterialDict materialDict, Element materialElement) {
        // 对于非原始物品，开始解析合成表
        if(GadgetsBm02Constant.ORI_FLAG_NO.equals(materialDict.getOriFlag())){
            queryDTO.setMeterialId(materialDict.getId());
            List<MaterialRecipe> recipeList  = materialRecipeRepository.selectMaterialRecipeByCondition(queryDTO);
            HashMap<String,Element> batchNoSet = new HashMap<>();
            for (MaterialRecipe recipe : recipeList) {
                if(!batchNoSet.containsKey(recipe.getBatchNo())){
                    Element targetElement = materialElement.addElement(GadgetsBm02Constant.MATERIAL_CONFIG.ELE_RECIPE);
                    targetElement.addAttribute(GadgetsBm02Constant.MATERIAL_CONFIG.ATTR_RECIPE_ENERGY_NUM,String.valueOf(recipe.getEnergyNum()));
                    targetElement.addAttribute(GadgetsBm02Constant.MATERIAL_CONFIG.ATTR_RECIPE_PRODUCT_UNIT,String.valueOf(recipe.getGoodNum()));
                    targetElement.addAttribute(GadgetsBm02Constant.MATERIAL_CONFIG.ATTR_RECIPE_ENERGY_TYPE,"EU");
                    batchNoSet.put(recipe.getBatchNo(),targetElement);
                }
                Element targetElement = batchNoSet.get(recipe.getBatchNo());
                targetElement
                        .addElement(GadgetsBm02Constant.MATERIAL_CONFIG.ELE_RECIPE_ITEM)
                        .addAttribute(GadgetsBm02Constant.MATERIAL_CONFIG.ATTR_ITEM_NAME,gadgetsBm02Utils.getMcMeterialDictItem(recipe.getMateriaId()).getDictName())
                        .addAttribute(GadgetsBm02Constant.MATERIAL_CONFIG.ATTR_ITEM_DEMAND_NUM,String.valueOf(recipe.getDemandNum()))
                        .addAttribute(GadgetsBm02Constant.MATERIAL_CONFIG.ATTR_ITEM_MOD,gadgetsBm02Utils.getMcMeterialDictItem(recipe.getMateriaId()).getModName());
            }
        }
    }

}
